package org.six11.ss.client;

import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.JApplet;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import org.jdom.Element;
import org.six11.ss.client.olive.OliveDrawingSurface;
import org.six11.ss.client.olive.OliveState;
import org.six11.ss.shared.Message;
import org.six11.util.Debug;
import org.six11.util.adt.MultiState;
import org.six11.util.adt.SynchronizedQueue;
import org.six11.util.gui.GlassMessagePane;
import org.six11.util.layout.FrontEnd;

import static org.six11.util.layout.FrontEnd.*;
import static org.six11.ss.shared.GameStatusConstants.*;

/**
 * The primary user interface component for the StellaSketch game. It is displayed in a web browser.
 * 
 * @author Gabe Johnson <johnsogg@cmu.edu>
 */
public class StellasketchApplet extends JApplet {

  private MultiState multiState;
  private NetworkThing networkThing;
  private TextConsole console;
  private JPanel cards;
  private Map<String, String> stateToCard;
  private GlassMessagePane glassMessagePane;

  /**
   * @throws HeadlessException
   */
  public StellasketchApplet() throws HeadlessException {
    this.stateToCard = new HashMap<String, String>();

    // also establish a mapping between the game state and which card to show
    stateToCard.put(STATUS_LOBBY, ClientStateNames.K_CARD_LOBBY);
    stateToCard.put(STATUS_PREGAME, ClientStateNames.K_CARD_PREGAME);
    stateToCard.put(STATUS_THEME, ClientStateNames.K_CARD_THEME);
    stateToCard.put(STATUS_WARN, ClientStateNames.K_CARD_WARN);
    stateToCard.put(STATUS_NOTIFY_ROLES, ClientStateNames.K_CARD_NOTIFY_ROLES);
    stateToCard.put(STATUS_PLAY, ClientStateNames.K_CARD_PLAY);
    stateToCard.put(STATUS_REPLAY, ClientStateNames.K_CARD_REPLAY);
    stateToCard.put(STATUS_TERMINAL, ClientStateNames.K_CARD_TERMINAL);
    stateToCard.put(STATUS_GAME_STATS, ClientStateNames.K_CARD_GAME_STATS);
    stateToCard.put(STATUS_NO_GAME, ClientStateNames.K_CARD_ROOM);
  }

  @Override
  public void init() {

    Debug.useColor = false;

    // the MultiState object is the grand-master dictionary of all data for this application.
    this.multiState = new MultiState();

    multiState.setValue(ClientStateNames.K_TRANSMIT_QUEUE, new SynchronizedQueue<Message>());
    multiState.setValue(ClientStateNames.K_PEN_QUEUE, new SynchronizedQueue<Element>());
    multiState.setValue(ClientStateNames.K_ROOM_LIST, new ArrayList<RoomInfo>());
    multiState.setValue(ClientStateNames.K_THEME_LIST, new ArrayList<ThemeChoice>());

    try {
      // When running the applet from an appletviewer, the getCodeBase() returns nonsense.
      URL u = getCodeBase();
      URL u2 = new URL(u.getProtocol(), u.getHost(), u.getPort(), u.getPath() + "msg");
      //String urlStr = "http://localhost:8080/ss/msg";
      
       networkThing = new NetworkThing(multiState, u2); // constructor starts things.
      // networkThing = new NetworkThing(multiState, new URL(urlStr)); // constructor starts things.

    } catch (MalformedURLException ex) {
      ex.printStackTrace();
    }

    // Command handlers are components that respond to local user actions like "join room X" or
    // "send my sketch data". They are responsible for reading client state and sending Messages to
    // the server.
    Map<String, CommandHandler> commandHandlers = new HashMap<String, CommandHandler>();
    multiState.setValue(ClientStateNames.K_COMMAND_HANDLERS, commandHandlers);
    commandHandlers.put("join", new JoinRoomCommandHandler(multiState));
    commandHandlers.put("list", new ListRoomCommandHandler(multiState));
    commandHandlers.put("default", new CommandHandler(multiState));
    commandHandlers.put("leave", new LeaveRoomCommandHandler(multiState));
    commandHandlers.put("nick", new NickCommandHandler(multiState));
    commandHandlers.put("who", new WhoCommandHandler(multiState));
    
    // Message handlers are components that respond to foreign actions from the server. This could
    // be in reaction to the local user's action (such as a message notifying the user they
    // successfully joined a room). Alternately, messages from the server could be notification of
    // other user actions (e.g. Bob entered the room you are in) or of game state (e.g. The next
    // round begins in 10 seconds).
    networkThing.addMessageHandler("roomList", new ListRoomMessageHandler(multiState));
    networkThing.addMessageHandler("roomJoined", new JoinRoomMessageHandler(multiState));
    networkThing.addMessageHandler("roomLeft", new LeaveRoomMessageHandler(multiState));
    networkThing.addMessageHandler("game-status", new GameStatusMessageHandler(multiState));
    networkThing.addMessageHandler("chat", new ChatMessageHandler(multiState));
    networkThing.addMessageHandler("pen", new PenMessageHandler(multiState));
    networkThing.addMessageHandler("themeOptionsList", new ThemeOptionsMessageHandler(multiState));
    networkThing.addMessageHandler("warn", new WarnMessageHandler(multiState));
    networkThing.addMessageHandler("role", new RoleMessageHandler(multiState));
    networkThing.addMessageHandler("labelOK", new LabelReplyMessageHandler(multiState));
    networkThing.addMessageHandler("labels", new LabelsMessageHandler(multiState));
    networkThing.addMessageHandler("scores", new ScoresMessageHandler(multiState));
    networkThing.addMessageHandler("clue", new ClueMessageHandler(multiState));
    networkThing.addMessageHandler("doneSketching", new SketchDoneMessageHandler(multiState));
    networkThing.addMessageHandler("roomMembership", new RoomMembershipMessageHandler(multiState));
    
    multiState.addChangeListener(ClientStateNames.K_GAME_STATUS, new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        String st = multiState.getString(ClientStateNames.K_GAME_STATUS);
        showCard(stateToCard.get(st));
      }
    });

    makeUI();
    String personID = getParameter("personID");
    multiState.setValue(ClientStateNames.K_MY_ID, personID);
    login(personID);
    showCard(ClientStateNames.K_CARD_ROOM);
  }

  /**
   * Gains access to the central outgoing message queue. If you have questions, comments, demands,
   * or constructive criticism for the server, add your message to this queue and it will go out
   * very soon. If you register a listener with the appropriate type, you can even get an answer.
   * 
   * @return the main outbound message queue.
   */
  @SuppressWarnings("unchecked")
  public SynchronizedQueue<Message> getQueue() {
    return (SynchronizedQueue<Message>) multiState.getValue(ClientStateNames.K_TRANSMIT_QUEUE);
  }

  private void login(String personID) {
    bug("Logging in.");
    SynchronizedQueue<Message> queue = getQueue();
    Message msg = new Message("login");
    msg.addParam("personID", personID);
    bug("I would like to send this xml: " + msg.toXml());
    queue.add(msg);
  }

  private static void bug(String what) {
    Debug.out("StellasketchApplet", what);
  }

  private void showCard(String name) {
    bug("Attempting to show card in panel: '" + name + "'");
    CardLayout cardLayout = (CardLayout) cards.getLayout();
    cardLayout.show(cards, name);
  }

  private void makeUI() {

    glassMessagePane = new GlassMessagePane();
    glassMessagePane.setFont(new Font("Dialog", Font.BOLD, 48));
    setGlassPane(glassMessagePane);
    final Timer timer = new Timer(2000, new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        glassMessagePane.clear();
      }
    });
    timer.setRepeats(false);
    multiState.addChangeListener(ClientStateNames.K_GLASS_MESSAGE, new PropertyChangeListener() {
      public void propertyChange(PropertyChangeEvent evt) {
        String msg = (String) evt.getNewValue();
        if (msg != null) {
          glassMessagePane.setMessage(msg);
          timer.start();
        }
      }
    });

    String CARDS = "cards";
    String TEXT = "text io";
    FrontEnd fe = new FrontEnd();
    // the sketching card
    PlayCard playCard = new PlayCard(multiState);
    multiState.setValue(ClientStateNames.K_CARD_PLAY, playCard);

    // the 'pick a room' panel
    RoomPanel roomPanel = new RoomPanel(multiState);
    multiState.setValue(ClientStateNames.K_CARD_ROOM, roomPanel);

    JPanel pregamePanel = new JPanel();
    pregamePanel.setBackground(Color.WHITE);
    pregamePanel.add(new JLabel("Waiting for other players to show up..."));
    multiState.setValue(ClientStateNames.K_CARD_PREGAME, pregamePanel);

    // the 'pick a theme' panel
    ThemePanel themePanel = new ThemePanel(multiState);
    multiState.setValue(ClientStateNames.K_CARD_THEME, themePanel);

    // the 'hang out for a second' panel
    LobbyPanel lobbyPanel = new LobbyPanel(multiState);
    multiState.setValue(ClientStateNames.K_CARD_LOBBY, lobbyPanel);

    WarnPanel warnPanel = new WarnPanel(multiState);
    // warnPanel.add(new JLabel("warn panel"));
    multiState.setValue(ClientStateNames.K_CARD_WARN, warnPanel);

    NotifyPanel notifyPanel = new NotifyPanel(multiState);
    multiState.setValue(ClientStateNames.K_CARD_NOTIFY_ROLES, notifyPanel);

    ReplayPanel replayPanel = new ReplayPanel(multiState);
    multiState.setValue(ClientStateNames.K_CARD_REPLAY, replayPanel);

    ScorePanel terminalPanel = new ScorePanel(multiState);
    multiState.setValue(ClientStateNames.K_CARD_TERMINAL, terminalPanel);

    // JPanel resultsPanel = new JPanel();
    // resultsPanel.add(new JLabel("game results panel"));
    multiState.setValue(ClientStateNames.K_CARD_GAME_STATS, terminalPanel);

    // the text console (chat/labeling component)
    console = new TextConsole(multiState);
    // assemble the 'control box', which is just a component that can show whichever content is
    // necessary depending on the game phase.
    cards = new JPanel() {
      public Dimension getPreferredSize() {
        Dimension parentPref = super.getPreferredSize();
        return new Dimension(400, parentPref.height);
      }
    };
    cards.setBackground(Color.WHITE);
    CardLayout cardLayout = new CardLayout();
    cards.setLayout(cardLayout);
    cards.add(playCard, ClientStateNames.K_CARD_PLAY);
    cards.add(pregamePanel, ClientStateNames.K_CARD_PREGAME);
    cards.add(roomPanel, ClientStateNames.K_CARD_ROOM);
    cards.add(themePanel, ClientStateNames.K_CARD_THEME);
    cards.add(lobbyPanel, ClientStateNames.K_CARD_LOBBY);
    cards.add(warnPanel, ClientStateNames.K_CARD_WARN);
    cards.add(notifyPanel, ClientStateNames.K_CARD_NOTIFY_ROLES);
    cards.add(replayPanel, ClientStateNames.K_CARD_REPLAY);
    cards.add(terminalPanel, ClientStateNames.K_CARD_TERMINAL);
    // cards.add(resultsPanel, ClientStateNames.K_CARD_GAME_STATS);

    fe.add(cards, CARDS);
    fe.add(console, TEXT);

    // ------------------------------------- constraints for the control box
    fe.addRule(ROOT, N, CARDS, N);
    fe.addRule(ROOT, W, CARDS, W);
    fe.addRule(ROOT, S, CARDS, S);

    // ------------------------------------- constraints for the text box
    fe.addRule(ROOT, N, TEXT, N);
    fe.addRule(ROOT, S, TEXT, S);
    fe.addRule(ROOT, E, TEXT, E);
    fe.addRule(CARDS, E, TEXT, W);

    add(fe);
  }

  @Override
  public void destroy() {
    try {
      bug("Logging out...");
      String personID = getParameter("personID");
      SynchronizedQueue<Message> queue = getQueue();
      Message msg = new Message("logout");
      msg.addParam("personID", personID);
      bug("I would like to send this xml: " + msg.toXml());
      queue.add(msg);
    } catch (Exception ex) {

    }
  }

}
